package com.modai.proxy;

import com.alibaba.fastjson.JSON;
import com.modai.remote.dto.CommonResponse;
import com.modai.remote.dto.UserJwt;
import org.junit.platform.commons.util.FunctionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import javax.annotation.PostConstruct;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

/**
 * @author ：modai
 * @date ：Created in 2021-6-5 23:53
 * @description：远程服务的代理
 */
@Component
public class RemoteApiProxy {

    @Value("${remote.uri}")
    private String uri;

    @Value("${remote.port}")
    private String port;

    private static String remoteUri;
    private static String remotePort;

    @Qualifier("restTemplate")
    @Autowired
    private RestTemplate template;

    private static RestTemplate restTemplate;

    private static String AUTH_TOKEN;

    private static final Pattern COMPILE = Pattern.compile("\\{.*}");


    @PostConstruct
    public void init() {
        // 赋值静态变量
        remoteUri = uri;
        remotePort = port;
        restTemplate = template;

        ResponseEntity<CommonResponse<UserJwt>> responseEntity = restTemplate.exchange(remoteUri + ":" + remotePort + "/login?username=admin&password=123",
                HttpMethod.POST,
                null,
                new ParameterizedTypeReference<CommonResponse<UserJwt>>() {
                });
        AUTH_TOKEN = Objects.requireNonNull(responseEntity.getBody()).getResult().getAccessToken();
    }


    public static <T> T getApi(Class<T> apiClazz) {
        ClassLoader cl = apiClazz.getClassLoader();
        Class<?>[] interfaces = new Class[]{apiClazz};

//        final String[] remoteUrl = {remoteUri + ":" + remotePort};
        AtomicReference<String> remoteUrl = new AtomicReference<>(remoteUri+":"+remotePort);
        RequestMapping rm = apiClazz.getAnnotation(RequestMapping.class);
        String[] paths = rm.value();
        // 如果有下级路径，则添加
        if (paths.length != 0) {
            remoteUrl.getAndUpdate(t->t+paths[0]);
        }

        Object o = Proxy.newProxyInstance(
                cl,
                interfaces,
                (proxy, method, args) -> {
                    // 获取请求类型 和 请求路径
                    AtomicReference<HttpMethod> requestMethod = new AtomicReference<>();

                    Arrays.stream(method.getAnnotations()).forEach(annotation -> {
                        if (annotation instanceof GetMapping) {
                            // 设置请求类型
                            requestMethod.set(HttpMethod.GET);
                            // 更新请求路径
                            String[] gmPaths = ((GetMapping) annotation).value();
                            if (gmPaths.length != 0) {
                                remoteUrl.getAndUpdate(t->t+gmPaths[0]);
                            }

                        } else if (annotation instanceof PutMapping) {
                            requestMethod.set(HttpMethod.PUT);
                            // 更新请求路径
                            String[] gmPaths = ((PutMapping) annotation).value();
                            if (gmPaths.length != 0) {
                                remoteUrl.getAndUpdate(t->t+gmPaths[0]);
                            }
                        } else if (annotation instanceof DeleteMapping) {
                            requestMethod.set(HttpMethod.DELETE);
                            // 更新请求路径
                            String[] gmPaths = ((DeleteMapping) annotation).value();
                            if (gmPaths.length != 0) {
                                remoteUrl.getAndUpdate(t->t+gmPaths[0]);
                            }
                        } else if (annotation instanceof PostMapping) {
                            requestMethod.set(HttpMethod.POST);
                            // 更新请求路径
                            String[] gmPaths = ((PostMapping) annotation).value();
                            if (gmPaths.length != 0) {
                                remoteUrl.getAndUpdate(t->t+gmPaths[0]);
                            }
                        } else if (annotation instanceof RequestMapping) {
                            RequestMapping requestMapping = (RequestMapping) annotation;
                            RequestMethod[] requestMethods = requestMapping.method();
                            if (requestMethods.length == 0) {
                                requestMethod.set(HttpMethod.GET);
                            } else {
                                requestMethod.set(HttpMethod.valueOf(requestMethods[0].name()));
                            }

                            // 更新请求路径
                            String[] gmPaths = ((RequestMapping) annotation).value();
                            if (gmPaths.length != 0) {
                                remoteUrl.getAndUpdate(t->t+gmPaths[0]);
                            }

                        }
                    });

                    // 获取请求参数 或 请求体
                    List<String> requestParams = new ArrayList<>();
                    AtomicReference<String> requestBody = new AtomicReference<>();
                    // 分析参数注解
                    if (args != null) {
                        IntStream.range(0,args.length).forEach(i->{
                            Parameter parameter = method.getParameters()[i];
                            Arrays.stream(parameter.getAnnotations()).forEach(annotation -> {
                                if (annotation instanceof PathVariable) {
                                    // 如果是path参数，需要替换掉url中的对应palceholder
                                    remoteUrl.getAndUpdate(t->COMPILE.matcher(t).replaceAll(args[i].toString()));
                                } else if (annotation instanceof RequestParam) {
                                    // 如果是请求参数，需要变为？变量名=变量值&变量名。。。将其以键值对的形式返回
                                    RequestParam requestParamAnnotation = (RequestParam) annotation;
                                    // 参数名
                                    String requestParam = requestParamAnnotation.value();
                                    // 参数值
                                    String value = args[i].toString();
                                    // 构造请求参数
                                    try {
                                        requestParams.add(requestParam + "=" +URLEncoder.encode( value, "utf-8"));
                                    } catch (UnsupportedEncodingException e) {
                                        e.printStackTrace();
                                    }
                                } else if (annotation instanceof RequestBody) {
                                    requestBody.set(JSON.toJSONString(args[i]));
                                }
                            });
                        });
                    }

                    // 在url中加入请求参数
                    String requestParamPath = StringUtils.arrayToDelimitedString(requestParams.toArray(), "&");
                    remoteUrl.getAndUpdate(t->t+"?"+requestParamPath);



                    // 返回值类型（泛型）
                    ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();


                    // 发送请求
                    // ps.请求参数需要主动附在url中，exchange的最后一个参数虽然是可以接收请求参数，但只能是去更改以传入的queryParam的值，并不能附加新的值
                    ResponseEntity<? extends ParameterizedType> responseEntity = restTemplate.exchange(
                            // 访问地址
                            remoteUrl.get(),
                            // 访问方法
                            requestMethod.get(),
                            // 访问头
                            new HttpEntity<>(
                                    requestBody.get(),
                                    new HttpHeaders() {{
                                        add("auth", AUTH_TOKEN);
                                        add("content-type", "application/json");
                                    }}),
                            // 返回的结果类型
                            ParameterizedTypeReference.forType(returnType)
                    );
                    return responseEntity.getBody();
                });

        return (T) o;
    }
}
